﻿using Abp.Application.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using KelonProud.Administration.Domain.Authorization;
using KelonProud.Administration.Domain.Common;
using KelonProud.Administration.Managers.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Routing;

namespace KelonProud.Administration.Services
{
    public interface INavigationProvider : IApplicationService
    {
        Navigation GetNavItemByRequest();

        Task<IEnumerable<MenuItem>> GetUserTopNavigations(string serachStr);

        IList<string> GetNavPathList();

    }

    public class KelonNavigationProvider : INavigationProvider
    {
        private readonly NavigationManager _navManager;
        private readonly UrlHelper _urlHelper;
        private readonly IActionContextAccessor _actionContextAccessor;
        private readonly UserManager _userManager;
        private readonly RoleManager _roleManager;

        public KelonNavigationProvider(IActionContextAccessor actionContextAccessor,
            NavigationManager navManager,
            UserManager userManager, 
            RoleManager roleManager)
        {
            _actionContextAccessor = actionContextAccessor;
            _navManager = navManager;
            _userManager = userManager;
            _roleManager = roleManager;
            _urlHelper = new UrlHelper(_actionContextAccessor.ActionContext);
        }

        [HttpGet]
        public IList<string> GetNavPathList()
        {
            var currNav = GetNavItemByRequest();
            var result = new List<string>();

            while (currNav != null)
            {
                result.Insert(0, currNav.Name);
                currNav = currNav.Parent;
            }

            return result;
        }

        [HttpGet]
        public Navigation GetNavItemByRequest()
        {
            var urlPath = _actionContextAccessor.ActionContext.HttpContext.Request.Host.Value;

            if (IsNotQueryRequest())
            {

                urlPath = _urlHelper.ActionContext.HttpContext.Request.Path.Value;

                //由于Html.ActionLink方法对于Action为Index生成的Url地址只包含Area和Controller，而不包含Index，造成根据Url地址无法获取菜单，从而无法判断权限
                //因此在问号前面插入字符串/Index
                if (urlPath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Count() == 2)
                {
                    urlPath = urlPath.IndexOf('?') > 0 ? urlPath.Insert(urlPath.IndexOf('?'), "/Index") : urlPath + "/Index";
                }

                //处理Area/Controller/Action/id
                if (urlPath.IndexOf('?') < 0 && System.Text.RegularExpressions.Regex.IsMatch(urlPath, "/\\d+$"))
                    urlPath = System.Text.RegularExpressions.Regex.Replace(urlPath, "(/\\d+$)", "");

            }

            var nav = _navManager.Query
                .FirstOrDefault(a => a.ActionUrl.HasValue() && a.ActionUrl.ToLower().Contains(urlPath.ToLower()));

            return nav ?? GetNavByLongestPath(urlPath);
        }

        [HttpGet]
        public async Task<IEnumerable<MenuItem>> GetUserTopNavigations(string serachStr)
        {
            var roleList = _roleManager.CurrentUserRoles.ToList();
            var result = new List<MenuItem>();

            var query = _navManager.Query.Where(w => w.Type == NavigationType.Menu&&w.Level==1&&!w.IsHide);

            if (serachStr.HasValue())
            {
                query = query.Where(w => w.Children.Any(a => a.Code.Contains(serachStr)
                                    || a.Name.Contains(serachStr))
                                    || w.Code.Contains(serachStr)
                                    || w.Name.Contains(serachStr));
            }
            var topmenus = query.ToList().Where(w=>w.Roles.Any(a=>roleList.Any(r=>r.RoleId==a.RoleId))).ToList();

            foreach (var menu in topmenus)
            {
                result.Add(new MenuItem
                {
                    Code = menu.Code,
                    Image = menu.Icon.HasValue() ? menu.Icon : "fa fa-file",
                    Name = menu.Name,
                    Order = menu.Order,
                    ActionUrl = menu.ActionUrl.HasValue() ? _urlHelper.Content(menu.ActionUrl) : "",
                    Items = await GetChildrenItem(menu.Id, serachStr)
                });
            }

            return result;
        }

        private async Task<List<MenuItem>> GetChildrenItem(int parentId, string serachStr)
        {
            var result = new List<MenuItem>();
            var roleList = _roleManager.CurrentUserRoles.ToList();
            var query = _navManager.Query.Where(w => w.ParentId == parentId&&w.Type== NavigationType.Menu && !w.IsHide);

            if (serachStr.HasValue())
            {
                query = query.Where(w => w.Code.Contains(serachStr)
                                         || w.Name.Contains(serachStr));
            }

            var menus = query.ToList().Where(w => w.Roles.Any(a => roleList.Any(r => r.RoleId == a.RoleId))).ToList();

            foreach (var menu in menus)
            {
                result.Add(item: new MenuItem
                {
                    Code = menu.Code,
                    Image = menu.Icon.HasValue() ? menu.Icon : "fa fa-file",
                    Name = menu.Name,
                    Order = menu.Order,
                    ActionUrl = menu.ActionUrl.HasValue() ? _urlHelper.Content(menu.ActionUrl) : "",
                    Items = await GetChildrenItem(menu.Id, serachStr)
                });
            }

            return result;
        }

        private bool IsNotQueryRequest()
        {
            return true;
        }

        private Navigation GetNavByLongestPath(string url)
        {
            int index = url.IndexOf('?');
            string path = (index >= 0 ? url.Substring(0, index) : url);

            var list = _navManager.List.Where(x => x.ActionUrl.HasValue() && x.ActionUrl.Equals(path, StringComparison.OrdinalIgnoreCase));

            return list.FirstOrDefault(x => IsMatchUrl(url, x.ActionUrl));
        }

        private bool IsMatchUrl(string url, string oraUrl)
        {
            string tmpUrl = GetStaticLocation(oraUrl).ToLower();

            if (tmpUrl.EndsWith("="))
            {
                return url.StartsWith(tmpUrl);
            }

            if (url.Equals(tmpUrl, StringComparison.OrdinalIgnoreCase))
                return true;

            if (tmpUrl.IndexOf("?", StringComparison.Ordinal) > 0)
                tmpUrl += "&";
            else
                tmpUrl += "?";
            return url.StartsWith(tmpUrl, StringComparison.OrdinalIgnoreCase);
        }

        private string GetStaticLocation(string url)
        {
            if (url.Length > 0)
            {
                var index = url.IndexOf("=", StringComparison.Ordinal);
                if (index > 0)
                {
                    return url.Substring(0, index + 1);
                }
            }

            return url;
        }
    }
}
